#include "lib/common.h"
#include "message_objecte.h"

#define    MAXLINE     4096
#define    KEEP_ALIVE_TIME  10
#define    KEEP_ALIVE_INTERVAL  3
#define    KEEP_ALIVE_PROBETIMES  3


int main(int argc, char **argv) 
{
    if (argc != 2) 
    {
        error(1, 0, "usage: tcpclient <IPaddress>");
    }

    // 创建了 TCP 套接字
    int socket_fd;
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    // 创建了 IPv4 目标地址，其实就是服务器端地址，注意这里使用的是传入参数作为服务器地址
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);

    // 向服务器端发起连接
    socklen_t server_len = sizeof(server_addr);
    int connect_rt = connect(socket_fd, (struct sockaddr *) &server_addr, server_len);
    if (connect_rt < 0) {
        error(1, errno, "connect failed ");
    }

    char recv_line[MAXLINE + 1];
    int n;

    fd_set readmask;
    fd_set allreads;


    struct timeval tv;
    int heartbeats = 0;

    //设置select定时器准备
    // 设置了超时时间为 KEEP_ALIVE_TIME，这相当于保活时间
    tv.tv_sec = KEEP_ALIVE_TIME;
    tv.tv_usec = 0;

    messageObject messageObject;

    // 初始化 select 函数的套接字
    FD_ZERO(&allreads);
    FD_SET(0, &allreads);
    FD_SET(socket_fd, &allreads);
    //处理心跳报文
    for (;;) 
    {
        readmask = allreads;
        //调用 select 函数，感知 I/O 事件,
        // 这里的 I/O 事件，除了套接字上的读操作之外，设置的超时事件。
        // 当 KEEP_ALIVE_TIME 这段时间到达之后，select 函数会返回 0
        int rc = select(socket_fd + 1, &readmask, NULL, NULL, &tv); //select I/O 复用函数自带定时器
        if (rc < 0) 
        {
            error(1, errno, "select failed");
        }
        // 客户端已经在 KEEP_ALIVE_TIME 这段时间内没有收到任何对当前连接的反馈，
        // 于是发起 PING 消息，尝试问服务器端：“喂，你还活着吗？”
        // 这里我们通过传送一个类型为 MSG_PING 的消息对象来完成 PING 操作，
        // 之后我们会看到服务器端程序如何响应这个 PING 操作；
        if (rc == 0)
        {
            if (++heartbeats > KEEP_ALIVE_PROBETIMES) 
            {
                error(1, 0, "connection dead\n");
            }
            printf("sending heartbeat #%d\n", heartbeats);
            messageObject.type = htonl(MSG_PING);
            rc = send(socket_fd, (char *) &messageObject, sizeof(messageObject), 0);
            if (rc < 0) 
            {
                error(1, errno, "send failure");
            }
            tv.tv_sec = KEEP_ALIVE_INTERVAL;
            continue;
        }
        //客户端在接收到服务器端程序之后的处理
        if (FD_ISSET(socket_fd, &readmask)) 
        {
            n = read(socket_fd, recv_line, MAXLINE);
            if (n < 0) 
            {
                error(1, errno, "read error");
            } else if (n == 0) 
            {
                error(1, 0, "server terminated \n");
            }
            printf("received heartbeat, make heartbeats to 0 \n");
            heartbeats = 0;
            tv.tv_sec = KEEP_ALIVE_TIME;
        }
    }
}

